package org.fixies.spreadsheet.converter;

import org.fixies.utils.ClassUtils;
import org.fixies.utils.Log;
import org.fixies.utils.UnexpectedProblemException;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.fixies.utils.StringUtils.concat;

@SuppressWarnings("unchecked")
public class ConverterFactory {

    private Map<Type, Converter<?>> pool_ = new HashMap<Type, Converter<?>>();

    public void registerDefault() {

        register(IDataSetConverter.class);
        register(ITableConverter.class);
        register(ITableMapConverter.class);
        register(ListMapConverter.class);
        register(MapConverter.class);
        register(ParameterMapConverter.class);
        register(NopSheetConverter.class);
        register(NopDataSetConverter.class);
        register(ITableMapConverter.class);

    }

    public void register(List<Class<? extends Converter<?>>> converters) {
        for (Class<? extends Converter<?>> c : converters) {
            register(c);
        }
    }

    public void register(Class<? extends Converter<?>> clazz) {

        Type targetType = getGenericReturnType(clazz);
        Converter<?> instance = (Converter) ClassUtils.newInstance(clazz);
        Log.debug("register target type=[" + targetType + "] converter=[" + clazz + "]");
        pool_.put(targetType, instance);

    }

    public DataSetConverter<?> getDataSetConverter(Field to) {
        Converter<?> converter = getConverter(to);
        if (converter instanceof DataSetConverter<?>) {
            return (DataSetConverter<?>) converter;
        }
        throw new UnexpectedProblemException("invalid converter.[", converter, "]");
    }

    public SheetConverter<?> getSheetConverter(Field to) {
        Converter<?> converter = getConverter(to);
        if (converter instanceof SheetConverter<?>) {
            return (SheetConverter<?>) converter;
        }
        throw new UnexpectedProblemException("invalid converter.[", converter, "]");
    }

    public Converter<?> getConverter(Field to) {
        Type targetType = to.getGenericType();
        Converter<?> converter = pool_.get(targetType);
        if (converter == null) {
            throw new IllegalArgumentException(concat("cant get converter. type=[", targetType, "]"));
        }
        Log.debug("getConverter target type=[", targetType, "] converter=[", converter, "]");
        return converter;
    }

    private Type getGenericReturnType(Class<? extends Converter<?>> clazz) {
        try {
            return clazz.getMethod("convert").getGenericReturnType();
        } catch (SecurityException e) {
            throw new UnexpectedProblemException(e);
        } catch (NoSuchMethodException e) {
            throw new UnexpectedProblemException(e);
        }
    }
}
